#ifndef _VECTOR_C
#define _VECTOR_C

#include "_Vector.h"

/* Constructor */
void
initVector(Vector *initThis)
{
    void **vectorData = malloc(_VECTOR_START_CAPACITY * sizeof(void *));
    
    if(vectorData == NULL) {
        fprintf(stderr, 
                "%s:%d Error: malloc returned NULL\n", 
                __FILE__, 
                __LINE__);

        free(vectorData);
    }

    initThis->data = vectorData;
    initThis->length = 0;
    initThis->capacity = _VECTOR_START_CAPACITY;
}

/* Destructor */
void
deleteVector(Vector *deleteThis)
{
    if(deleteThis != NULL) {
        free(deleteThis->data);
    }
}

int
_VectorResize(Vector *v)
{
    size_t newCapacity = (size_t)(v->length * _VECTOR_GROW_FACTOR);
    size_t newSize = newCapacity * sizeof(void *); /* Size in bytes */

    v->data = realloc(v->data, newSize);

    /* If realloc failed */
    if(v->data == NULL) { 
        fprintf(stderr, 
                "%s:%d Error: realloc returned NULL\n", 
                __FILE__, 
                __LINE__);

        return 0;
    }

    v->capacity = newCapacity;

    return 1;
}

void 
VectorAdd(Vector *v, void *addThis)
{
    if(v->length == v->capacity) {

        /* 
         * If unable to grow v, exit. 
         * _VectorResize will display an error.
         */
        if(!_VectorResize(v)) return;
    }

    v->data[v->length] = addThis;
    v->length++;
}

void *
VectorGet(Vector *v, size_t index)
{
    if(_boundsCheck(v, index)) {
        return v->data[index];
    } else {
        _indexError(index);
        return NULL;
    }
}

void *
VectorSet(Vector *v, size_t index, void *setThis)
{
    void *toReturn = NULL;
    
    if(_boundsCheck(v, index)) {
        toReturn = v->data[index];
        v->data[index] = setThis;
    } else {
        _indexError(index);
    }

    return toReturn;
}

void 
VectorInsert(Vector *v, size_t index, void *insertThis)
{
    size_t i;

    /* If trying to insert at the end */
    if(index == v->length) {
        VectorAdd(v, insertThis);
    } else {
        if(_boundsCheck(v, index)) {

            /* If v is full, resize v */
            if(v->length == v->capacity) {
    
                /* 
                 * If unable to grow v, exit. 
                 * _VectorResize will display an error.
                 */
                if(!_VectorResize(v)) return;
            }

            for(i = v->length; i > index; --i) {
                v->data[i] = v->data[i - 1];
            }

            v->data[i] = insertThis;
            v->length++;
        } else {
            _indexError(index);
        }
    }
}

void *
VectorExtract(Vector *v, size_t index)
{
    size_t i;
    void *toReturn = NULL;
    
    if(_boundsCheck(v, index)) {
        toReturn = v->data[index];

        if(v->length > 1) {

            /* I think length - 1 is correct, but it might just be length */
            for(i = index; i < v->length - 1; ++i) {
                v->data[i] = v->data[i + 1];
            }
        }
        v->length--;

        /* If we too much free space, resize */
        if(v->capacity > (size_t)(_VECTOR_SHRINK_FACTOR * v->length) &&
           v->capacity > _VECTOR_START_CAPACITY) {
            _VectorResize(v);
        }
    } else {
        _indexError(index);
    }

    return toReturn;
}

#endif
